package com.tingyukeji.fastpay.data.rsa;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

public class RSAUtil {
    /** */
    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    
    /** */
    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    
    /** */
    /**
     * 获取公钥的key
     */
    private static final String PUBLIC_KEY_NAME = "RSAPublicKey";
    
    /** */
    /**
     * 获取私钥的key
     */
    private static final String PRIVATE_KEY_NAME = "RSAPrivateKey";
    
    /** */
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    
    /** */
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    
    /**
     * 参数加密
     */
    public static final String PUBLIC_KEY_1 =
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlvZRze2mVRYO1IKIPqdND kTdAWeXvfRioM6Ew8c4P48XAHYhGdGG9sAoMTmpCuf05TFyXhdzWo33hCO+k AepER6R6EwlpbUntJueOlMyRlmWYh6TFUlYlVC8ZVnoOKBtE9rq908+2dkWx nyA4rCRjbBi02lZ1a+W3SLUsoTf4eQIDAQAB";

    /**
     * 参数解密
     */
    public static final String PRIVATE_KEY_1 =
        "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOW9lHN7aZVF g7Ugog+p00ORN0BZ5e99GKgzoTDxzg/jxcAdiEZ0Yb2wCgxOakK5/TlMXJeF 3NajfeEI76QB6kRHpHoTCWltSe0m546UzJGWZZiHpMVSViVULxlWeg4oG0T2 ur3Tz7Z2RbGfIDisJGNsGLTaVnVr5bdItSyhN/h5AgMBAAECgYAetjY3LeNS ciWGPCOOBnzYhnwlXR8w4b8/FQ/SVDDgzOktooUKLhq22eat+HmaC/T/COdD 1kBMbTSxtZkbiqn/T+EhAav6zaV2CTMPJclz2BoaDN3z+L+DKwFnbwTTWBFK h8VALh9kQeMVmj/hEWZyh1EUcaKbgatI+eEMs4K7EQJBAPQOIWt9KvCGJtvH l5KepTxoGbHnONFFZMtzP0QizxTb7GD4XjRYmhXCJDwKQZZ63b1tBtc9vjwe f4UPJ27F4gUCQQDw/BhYn7hgacj2YgWnBf1e7heDMVMnOk5TXFFHT1Eqbg1P uW77YD/XqirYmKErH8d+WfyJIQoMMZ6k/tzTycLlAkBqfzgKJaZ1KrUONrxn mJ7WCz8bKKDcgQGI3hN/cgkXOxidAARrpzYlxH/3SypvtdHI9SiFCEZZCZ3v vDsi1WrZAkEAxoWW9ONXO63JzRcBOxAXI24W1o/6xZUl00x49LrlzYNt2eCU CefHkD3yIA7Urrb2XCN7g+hA47y4yYnfl2Vd9QJBAKltR/HjuXW8yRraX+Uf gbMJqNtgtkSVzzSbqx/7W1c77RwcHqcfhfrzYwWVNXgIdP9X+8UTUP+86gRy gavImPk=";
    
    /**
     * 返回内容加密
     */
    public static final String PUBLIC_KEY_2 =
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8VGuMwXOrg12iRcopWvvZ hie97kLmex6e+K4nQQBorAobUI90breLX6xkZaKqhpfRygnuWY05OtCtdCw6 kwoqdrNs27pepYQRZc4IqoF6T8mWEHUBzPRmybWWec/pIszyQ/QmXd3Z8d/l fqPwxQz4U7rydzX3CSxxrV1iq+5xDQIDAQAB";
    
    /**
     * 返回内容解密
     */
    public static final String PRIVATE_KEY_2 =
        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALxUa4zBc6uD XaJFyila+9mGJ73uQuZ7Hp74ridBAGisChtQj3Rut4tfrGRloqqGl9HKCe5Z jTk60K10LDqTCip2s2zbul6lhBFlzgiqgXpPyZYQdQHM9GbJtZZ5z+kizPJD 9CZd3dnx3+V+o/DFDPhTuvJ3NfcJLHGtXWKr7nENAgMBAAECgYEAkoSvQ8vB nP9P2o6nUHNbmwk3ZBm47K1+zjk07I8yrTyjhEqxwOjeXU1qxpUX78lU1j90 aH49U/gBH+FRA6Uf8z+YapYySPGny+6+zJVFWzARBlVYBjLzJkquQB2pH8dW vfeJNQmOG7dstc4itvWwHZdVTcTAqKa/YuD1Gk8MroECQQD19egtjBfQ6ThU k7OSNrb+yg6uYOgl2d3+GC7FGJ+B9Yn7x/IQRtzGyoGrr5ib/UtwBVnQEVIJ 27LSbAH1KVqhAkEAxARRILujmgrkHt2uKPoMxRXE9Rp2xnjvlXY8+T1l+VEg j+pOq0D0ZOWdcxVBfTNtfQ2FheXPpIhVajV/9pNK7QJAC4pdOX3gRDIdLYK8 ozij/3J1m+L6IYSGcPyrfR35pfxiXAj2p6LbiwxHZ0nW7ELHPltR6nxVkBbZ 3hupP9qP4QJAX6HTiurA6NjDbyOsYqWMMWNrkkF1DWPXT8499UO1Sqv//PbN ptOr6MX1yT3nk5PRqR89s5PpeFZjhcvQQJagCQJANQ0fIorHQ/Z+lUtyV+T3 m+QOku7VINQMx04MbLaRgSNGCgmY12fTIUMwO90aTovbjiTKyoWTYU4bzH2G WuSEDQ==";
    
    /** */
    /**
     * <p>
     * 生成密钥对(公钥和私钥)
     * </p>
     * 
     * @return
     * @throws Exception
     */
    public static Map<String, Object> genKeyPair()
        throws Exception {
        KeyPairGenerator keyPairGen =
            KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY_NAME, publicKey);
        keyMap.put(PRIVATE_KEY_NAME, privateKey);
        return keyMap;
    }
    
    /** */
    /**
     * <p>
     * 用私钥对信息生成数字签名
     * </p>
     * 
     * @param data 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * 
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey)
        throws Exception {
        byte[] keyBytes = Base64.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return Base64.encode(signature.sign());
    }
    
    /** */
    /**
     * <p>
     * 校验数字签名
     * </p>
     * 
     * @param data 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign 数字签名
     * 
     * @return
     * @throws Exception
     * 
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
        throws Exception {
        byte[] keyBytes = Base64.decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64.decode(sign));
    }
    
    /** */
    /**
     * <P>
     * 私钥解密
     * </p>
     * 
     * @param encryptedData 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData,
        String privateKey)
        throws Exception {
        byte[] keyBytes = Base64.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
//        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache =
                    cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            }
            else {
                cache =
                    cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
    
    /** */
    /**
     * <p>
     * 公钥解密
     * </p>
     * 
     * @param encryptedData 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData,
        String publicKey)
        throws Exception {
        byte[] keyBytes = Base64.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache =
                    cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            }
            else {
                cache =
                    cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
    
    /** */
    /**
     * <p>
     * 公钥加密
     * </p>
     * 
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
        throws Exception {
        byte[] keyBytes = Base64.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
//        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            }
            else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }
    
    /** */
    /**
     * <p>
     * 公钥加密
     * </p>
     * 
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey1(byte[] data, String publicKey)
        throws Exception {
        String result = "";
        byte[] keyBytes = Base64.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
//        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            }
            else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        result = Base64.encodeToString(out.toByteArray(), false);
        out.close();
        return result;
    }
    
    /** */
    /**
     * <p>
     * 私钥加密
     * </p>
     * 
     * @param data 源数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
        throws Exception {
        byte[] keyBytes = Base64.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            }
            else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }
    
    /** */
    /**
     * <p>
     * 获取私钥
     * </p>
     * 
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
        throws Exception {
        Key key = (Key)keyMap.get(PRIVATE_KEY_NAME);
        return Base64.encode(key.getEncoded());
    }
    
    /** */
    /**
     * <p>
     * 获取公钥
     * </p>
     * 
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)
        throws Exception {
        Key key = (Key)keyMap.get(PUBLIC_KEY_NAME);
        return Base64.encode(key.getEncoded());
    }
    
    public static void main(String[] args)
        throws Exception {
    	
    	System.out.println("-----------AAP端参数封装-----------");
    	String params = "{'mobilePhone': '18971496950','type': 1}";
    	System.out.println("params=" + params);
    	
        System.out.println("\r\n-----------AAP端公钥（参数加密）-----------");
        byte[] encryptByte = params.getBytes();
        String encryptStr = RSAUtil.encryptByPublicKey1(encryptByte, RSAUtil.PUBLIC_KEY_1);
        System.out.println("加密后参数：\r\n" + encryptStr);
        
        System.out.println("\r\n-----------服务端私钥（参数解密）-----------");
        byte[] decryptByte = RSAUtil.decryptByPrivateKey(Base64.decode(encryptStr), RSAUtil.PRIVATE_KEY_1);
        String decryptStr = new String(decryptByte);
        System.out.println("解密后参数: \r\n" + decryptStr);
        
        System.out.println("\r\n-----------服务端返回内容封装-----------");
        String contentResponse = "{'code':1,'message':'获取成功','result':{'validateCode':'3618'},'success':true,'timestamp':'2018-11-24 03:51:12'}";
        System.out.println(contentResponse);
        
        System.out.println("\r\n-----------服务端公钥（返回内容加密）-----------");
        byte[] encryptByte_2 = contentResponse.getBytes();
        String encryptStr_2 = RSAUtil.encryptByPublicKey1(encryptByte_2, RSAUtil.PUBLIC_KEY_2);
        System.out.println("加密后返回内容: \r\n" + encryptStr_2);
        
        System.out.println("\r\n-----------APP端私钥（返回内容解密）-----------");
        byte[] decryptByte_2 = RSAUtil.decryptByPrivateKey(Base64.decode(encryptStr_2), RSAUtil.PRIVATE_KEY_2);
        String decryptStr_2 = new String(decryptByte_2);
        System.out.println("解密后返回内容: \r\n" + decryptStr_2);
        
    }
    
}
